package com.jfunit.java.helper;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jfunit.java.annotation.Aspect;
import com.jfunit.java.annotation.Service;
import com.jfunit.java.annotation.Transaction;
import com.jfunit.java.proxy.Proxy;
import com.jfunit.java.proxy.ProxyManager;
import com.jfunit.java.proxy.TransactionProxy;

/**
 * 
 * @author Administrator
 *
 */
public final class AopHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

    static {
        System.out.println("======================================");
        try {
            System.out.println("AopHelper-0,start:");
            Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
            System.out.println("AopHelper-1,proxyMap:"+proxyMap);
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
            System.out.println("AopHelper-2,targetMap:"+targetMap);
            for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
                Class<?> targetClass = targetEntry.getKey();
                List<Proxy> proxyList = targetEntry.getValue();

                Object proxy = ProxyManager.createProxy(targetClass, proxyList);
                System.out.println("AopHelper-3,targetClass:"+targetClass+",proxy:"+proxy);
                BeanHelper.setBean(targetClass, proxy);
            }
            System.out.println("AopHelper-0,start:");
        } catch (Exception e) {
            LOGGER.error("aop failure", e);
        }

    }

    /**
     * 获取需要被代理的 所有 class 对象
     * 
     * @return key= 切面 ,value = 该切面所增强的所有 class 对象
     * @throws Exception
     */
    private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(proxyMap);
        addTransactionProxy(proxyMap);
        return proxyMap;
    }

    private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxyMap)  throws Exception {
        Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(Aspect.class);

        for (Class<?> proxyClass : proxyClassSet) {
            if (proxyClass.isAnnotationPresent(Aspect.class)) {// 判断该class 是否有 aspect注解(是否是一个切面标注)
                Aspect aspect = proxyClass.getAnnotation(Aspect.class);// 获取注解类
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);// 由于 aspect 的值是接收一个
                                                                            // 注解类,所以这里是获取到
                                                                            // 使用该注解类的所有class
                proxyMap.put(proxyClass, targetClassSet);
            }
        }
    }
    
    private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxyMap)  throws Exception{
        Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
        proxyMap.put(TransactionProxy.class, serviceClassSet);
    }

    /**
     * 获取 aspect 设置的 注解类,类型的所有class 集合
     * 
     * @param aspect
     * @return
     * @throws Exception
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
        Class<? extends Annotation> annotation = aspect.value();
        // Aspect 是对一类注解类 进行拦截,这里是 不为空并且不是 aspect注解,就可以获取该注解
        if (annotation != null && !annotation.equals(Aspect.class)) {
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
        }
        return targetClassSet;
    }

    /**
     * 把 class 和 增强它的切面实例进行关联
     * 
     * @param proxyMap 切面对应需要增强的class集合.
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap)
            throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
        for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
            Class<?> proxyClass = proxyEntry.getKey();// 获取切面class
            Set<Class<?>> targetClassSet = proxyEntry.getValue();// 切面所需要增强的 class
                                                                 // 对象(也就是我们需要把该切面在哪些实例上增强的对象class)
            for (Class<?> targetClass : targetClassSet) {
                System.out.println("proxyClass-------:"+proxyClass);
                Proxy proxy = (Proxy) proxyClass.newInstance();// 创建该切面的实例
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(proxy);
                } else {
                    List<Proxy> proxyList = new ArrayList<Proxy>();
                    proxyList.add(proxy);
                    targetMap.put(targetClass, proxyList);
                }
            }
        }
        return targetMap;
    }
}
